/*
 * Copyright (c) 2020 ymyuan
 * Cloud is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package com.totem.base.exception;

import com.totem.base.constants.ErrorCode;
import com.totem.base.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 统一异常处理器
 *
 * @author raymond
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public final class GlobalExceptionHandler {
    private GlobalExceptionHandler(){}

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public static Result methodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response) {
        List<String> messageList = ex.getBindingResult().getAllErrors().stream().map(objectError -> objectError.getDefaultMessage()).collect(Collectors.toList());
        return buildBody(ex, ErrorCode.ERROR, messageList.toString(), request.getRequestURI(), request.getMethod(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({ServletRequestBindingException.class})
    public static Result ServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response) {
        return buildBody(ex, ErrorCode.ERROR, ex.getMessage(), request.getRequestURI(), request.getMethod(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({BaseException.class})
    public static Result baseException(BaseException ex, HttpServletRequest request, HttpServletResponse response) {
        ErrorCode errorCode = ErrorCode.getResultEnum(ex.getCode());
        String traceMessage = ex.getTraceMessage();
        if(StringUtils.isBlank(traceMessage)){
            traceMessage = ex.getMessage();
        }
        return buildBody(ex, errorCode, traceMessage, request.getRequestURI(), request.getMethod(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({SQLException.class})
    public static Result sqlException(SQLException ex, HttpServletRequest request, HttpServletResponse response) {
        return buildBody(ex, ErrorCode.ERROR, ex.getMessage(), request.getRequestURI(), request.getMethod(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({Exception.class})
    public static Result exception(Exception ex, HttpServletRequest request, HttpServletResponse response) {
        return buildBody(ex, ErrorCode.ERROR, ex.getMessage(), request.getRequestURI(), request.getMethod(), HttpStatus.BAD_REQUEST);
    }

    private static Result buildBody(Throwable t, ErrorCode errorCode, String detailMessage, String uri, String method, HttpStatus httpStatus) {
        Result result = Result.error(errorCode.getCode(), errorCode.getMessage()).detail(detailMessage).uri(uri).method(method);
        log.error("exception ==>", t);
        log.error("result ==> {}", result);
        return result;
    }
}
